home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
drdobbs
/
1990
/
11
/
ayers.asc
next >
Wrap
Text File
|
1990-10-12
|
22KB
|
750 lines
_THE MVC PARADIGMN AND SMALLTALK/V_
by Kenneth E. Ayers
[LISTING ONE]
open
| frame |
appName := String new.
saved := true.
editorPen := Pen new.
imagePen := Pen new.
frame := (Display boundingBox extent // 6)
extent:(Display boundingBox extent * 2 // 3).
topPane := TopPane new
model:self;
label:self label;
menu:#windowMenu;
minimumSize:frame extent;
yourself.
topPane addSubpane:
(listPane := ListPane new
model:self;
name:#appList;
change:#appSelection:;
returnIndex:false;
menu:#listMenu;
framingRatio:(0 @ 0 extent:1/4 @ (2/3));
yourself).
topPane addSubpane:
(imagePane := GraphPane new
model:self;
name:#initImage:;
menu:#noMenu;
framingRatio:(0 @ (2/3) extent:1/4 @ (1/3));
yourself).
topPane addSubpane:
(editorPane := GraphPane new
model:self;
name:#initEditor:;
menu:#editorMenu;
change:#editIcon:;
framingRatio:(1/4 @ 0 extent:3/4 @ 1);
yourself).
topPane reframe:frame.
topPane dispatcher openWindow scheduleWindow.
[LISTING TWO]
open
| frame listWid listHgt |
saved := true.
editorPen := Pen new.
imagePen := Pen new.
frame := (Display boundingBox extent // 6)
extent:(Display boundingBox extent * 2 // 3).
listWid := SysFont width * 10.
listHgt := SysFont height * 10.
topPane := TopPane new
label:self label;
model:self;
menu:#windowMenu;
minimumSize:frame extent;
yourself.
topPane addSubpane:
(listPane := ListPane new
model:self;
name:#appList;
change:#appSelection:;
returnIndex:false;
menu:#listMenu;
framingBlock:[:aFrame |
aFrame origin
extent:listWid @ listHgt];
yourself).
topPane addSubpane:
(imagePane := GraphPane new
model:self;
name:#initImage:;
menu:#noMenu;
framingBlock:[:aFrame|
aFrame origin + (0 @ listHgt)
extent:(listWid
@ (aFrame height - listHgt))];
yourself).
topPane addSubpane:
(editorPane := GraphPane new
model:self;
name:#initEditor:;
menu:#editorMenu;
change:#editIcon:;
framingBlock:[:aFrame|
aFrame origin + (listWid @ 0)
extent:((aFrame width - listWid)
@ aFrame height)];
yourself).
topPane reframe:frame.
topPane dispatcher openWindow scheduleWindow.
[COMPLETE SMALLTALK/V SOURCE KEN AYERS'S ARTICLE IN NOVEMBER 1990 ISSUE OF
DDJ]
[Listing -- Class EmptyMenu]
Menu subclass: #EmptyMenu
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''.
"***************************************************************"
"** EmptyMenu instance methods **"
"***************************************************************"
popUpAt:aPoint
"An empty menu does nothing -- answer nil."
^nil.
popUpAt:aPoint for:anObject
"An empty menu does nothing -- answer nil."
^nil.
[Listing -- Class IconEditor]
Object subclass: #IconEditor
instanceVariableNames:
'scale cellSize cellOffset saved unZoom topPane listPane
editorPane imagePane iconLibrary iconName selectedIcon
gridForm editorPen imagePen'
classVariableNames:
'IconSize '
poolDictionaries:
'FunctionKeys CharacterConstants'.
"***************************************************************"
"** IconEditor class methods **"
"***************************************************************"
initialize
"Initialize the class variables."
IconSize isNil ifTrue:[IconSize := 32@32].
new
"Answer a new IconEditor."
self initialize.
^super new.
"***************************************************************"
"** IconEditor instance methods **"
"***************************************************************"
"-----------------------------"
"-- Window creation methods --"
"-----------------------------"
openOn:anIconLibrary
"Open an IconEditor window on the Dictionary
anIconLibrary."
iconLibrary := anIconLibrary.
self open.
open
"Open an IconEditor window."
| frame |
iconLibrary isNil
ifTrue:[self initLibrary].
iconName := String new.
saved := true.
editorPen := Pen new.
imagePen := Pen new.
frame := (Display boundingBox extent // 6)
extent:(Display boundingBox extent * 2 // 3).
topPane := TopPane new
model:self;
label:self label;
menu:#windowMenu;
minimumSize:frame extent;
yourself.
topPane addSubpane:
(listPane := ListPane new
model:self;
name:#iconList;
change:#iconSelection:;
returnIndex:false;
menu:#listMenu;
framingRatio:(0 @ 0 extent:1/4 @ (2/3));
yourself).
topPane addSubpane:
(imagePane := GraphPane new
model:self;
name:#initImage:;
menu:#noMenu;
framingRatio:(0 @ (2/3) extent:1/4 @ (1/3));
yourself).
topPane addSubpane:
(editorPane := GraphPane new
model:self;
name:#initEditor:;
menu:#editorMenu;
change:#editIcon:;
framingRatio:(1/4 @ 0 extent:3/4 @ 1);
yourself).
topPane reframe:frame.
topPane dispatcher openWindow scheduleWindow.
"----------------------------"
"-- Window support methods --"
"----------------------------"
windowMenu
"Answer the menu for the IconEditor window."
^Menu
labels:'collapse\cycle\frame\move\print\close' withCrs
lines:#(5)
selectors:#(collapse cycle resize
printWindow move closeIt).
listMenu
"Answer the menu for the form list pane."
^Menu
labels:'remove icon\create new icon\change size' withCrs
lines:#()
selectors:#(removeIt createIt resizeIt).
editorMenu
"Answer the menu for the Editor pane."
selectedIcon isNil ifTrue:[^EmptyMenu new].
^Menu
labels:('invert\border\erase\save\print') withCrs
lines:#(3)
selectors:#(invertIt borderIt eraseIt saveIt printIcon).
noMenu
"Answer a do-nothing menu."
^EmptyMenu new.
initEditor:aRect
"Inititalize the editor pane."
Display white:aRect.
^Form new extent:aRect extent.
initImage:aRect
"Inititalize the IconEditor image pane."
Display white:aRect.
^Form new extent:aRect extent.
iconList
"Answer a String Array containing the
names of the icons in the icon library."
^iconLibrary keys asArray.
iconSelection:anIconName
"The user has selected anIconName from
the list. Make it the selected icon
and re-initialize the editor."
| anIcon |
self saved ifFalse:[^self].
anIcon := iconLibrary at:anIconName ifAbsent:[nil].
anIcon isNil ifTrue:[^self].
selectedIcon := anIcon deepCopy.
iconName := anIconName.
selectedIcon extent = IconSize
ifTrue:[self displayIcon]
ifFalse:[
CursorManager execute change.
self resizeIt:selectedIcon extent].
editIcon:aPoint
"The select button has been pressed at aPoint.
If the cursor is in the editor image,
reverse that cell and all others that the
cursor passes over until the select button
is released."
| currX currY refX refY editing newX newY |
(editorPen clipRect containsPoint:Cursor offset)
ifFalse:[^self].
currX := -1.
currY := -1.
refX := editorPen clipRect origin x.
refY := editorPen clipRect origin y.
editing := true.
[editing]
whileTrue:[
newX := (Cursor offset x - refX) // scale.
newY := (Cursor offset y - refY) // scale.
((newX = currX) and:[newY = currY])
ifFalse:[
currX := newX.
currY := newY.
self setX:currX Y:currY.
saved := false].
editing :=
(editorPen clipRect containsPoint:Cursor offset)
and:[Terminal read ~= EndSelectFunction]].
selectedIcon
copy:imagePen clipRect
from:Display
to:0@0
rule:Form over.
"----------------------------"
"-- Window control methods --"
"----------------------------"
showWindow
"Redisplay the contents of the window's panes."
topPane collapsed
ifFalse:[self displayIcon].
activatePane
"If the cursor is in the Editor pane,
change its shape to a crosshair."
(editorPane frame containsPoint:Cursor offset)
ifTrue:[CursorManager hair change].
deactivatePane
"If the cursor is not in the Editor pane,
change its shape to the normal pointer."
(editorPane frame containsPoint:Cursor offset)
ifFalse:[CursorManager normal change].
reframePane:aPane
aPane == editorPane
ifTrue:[^self reframeEditor:aPane frame].
aPane == imagePane
ifTrue:[^self reframeImage:aPane frame].
zoom
"Zoom/unzoom the window."
| frame |
unZoom isNil
ifTrue:[ "Zoom to full screen"
unZoom := topPane frame.
frame := Display boundingBox]
ifFalse:[
frame := unZoom.
unZoom := nil].
CursorManager execute change.
topPane reframe:frame.
Scheduler resume.
close
"Prepare for closing the window."
self saved
ifFalse:[self saveIcon].
self release.
label
"Answer the window's label."
^'IconEditor (Ver 2.1 - 07/24/90 - KEA)'.
collapsedLabel
"Answer the window's label when collapsed."
^'IconEditor'.
"---------------------------------------"
"-- List-pane menu processing methods --"
"---------------------------------------"
createIt
"Create a new icon to edit."
self saved
ifTrue:[
self
eraseImage;
newIcon:self getIcon].
resizeIt
"Change the size of the icon."
| selection size |
self saved ifFalse:[^self].
selection := (Menu
labels: '8@8\16@16\32@32\64@64' withCrs
lines: #()
selectors:#(small medium large xLarge))
popUpAt:Cursor offset.
selection isNil ifTrue:[^self].
selection == #small ifTrue:[size := 8@8].
selection == #medium ifTrue:[size := 16@16].
selection == #large ifTrue:[size := 32@32].
selection == #xLarge ifTrue:[size := 64@64].
self resizeIt:size.
removeIt
"Remove the selectedIcon from the
icon library."
iconLibrary removeKey:iconName ifAbsent:[].
self
eraseEditor;
eraseImage;
changed:#iconList.
selectedIcon := nil.
iconName := ''.
"-----------------------------------------"
"-- Editor-pane menu processing methods --"
"-----------------------------------------"
eraseIt
"Erase icon."
(Form width:IconSize x height:IconSize y)
displayAt:imagePen clipRect origin rule:Form over.
(Form width:IconSize x * scale height:IconSize y * scale)
displayAt:editorPen clipRect origin rule:Form over.
gridForm
displayAt:editorPen clipRect origin rule:Form andRule.
self
border:editorPen clipRect
width:2
marksAt:(4 * scale).
self
border:imagePen clipRect
width:2
marksAt:0.
invertIt
"Invert the color of the icon."
selectedIcon := self getIcon reverse.
self displayIcon:selectedIcon.
saved := false.
borderIt
"Draw a border around the icon."
| inset |
(inset := Prompter prompt:'Inset?' default:'0') isNil
ifTrue:[^self].
Display border:(imagePen clipRect insetBy:inset asInteger).
self newIcon:self getIcon.
saved := false.
saveIt
"Save the new icon image."
self saveIcon.
self changed:#iconList.
listPane restoreSelected:iconName.
self activatePane.
printIcon
"Print the magnified icon from the editor pane."
CursorManager execute change.
(Form fromDisplay:(editorPen clipRect expandBy:4))
outputToPrinterUpright.
CursorManager normal change.
printWindow
"Print an image of the entire editor window."
CursorManager execute change.
(Form fromDisplay:topPane frame) outputToPrinterUpright.
CursorManager normal change.
"-----------------------------"
"-- Display support methods --"
"-----------------------------"
border:aRect width:aWid marksAt:aStep
"Draw a border, of width aWid, around aRect
with ruler marks ever aStep cells."
| box aForm orig wid hgt |
box := aRect expandBy:aWid.
0 to: aWid - 1 do:[:inset|
Display border:(box insetBy:inset)].
(aStep = 0)
ifTrue: ["** No ruler marks!!!! **" ^nil].
aForm := (Form width:aWid height:aWid) reverse.
orig := aRect origin.
wid := aRect width.
hgt := aRect height.
0 to:wid by:aStep do:[:x|
aForm
displayAt:(orig + (x @ ((aWid * 2) negated)));
displayAt:(orig + (x @ (hgt + aWid)))].
0 to:hgt by:aStep do:[:y|
aForm
displayAt:(orig + (((aWid * 2) negated) @ y));
displayAt:(orig + ((wid + aWid) @ y))].
editorBorder
"Draw the ruled border around the editor frame."
self
border:editorPen clipRect
width:2
marksAt:(4 * scale).
imageBorder
"Draw the solid border around the image frame."
self
border:imagePen clipRect
width:2
marksAt:0.
displayIcon
"Display the selectedIcon for editing."
selectedIcon isNil
ifFalse:[ self displayIcon:selectedIcon].
displayIcon:anIcon
"Display anIcon for editing."
CursorManager execute change.
self
eraseImage;
displayImage:anIcon;
eraseEditor;
editorBorder;
displayEditor:anIcon.
CursorManager normal change.
self activatePane.
displayEditor:anIcon
"Display anIcon in the editor pane."
| mask |
(anIcon
magnify:(0 @ 0 extent:anIcon extent)
by:scale @ scale)
displayAt:editorPen clipRect origin
rule:Form over.
mask := gridForm deepCopy reverse.
scale > 4
ifTrue:[
mask
displayOn:Display
at:(editorPen clipRect origin moveBy:(1@1))
clippingBox:editorPen clipRect
rule:Form orThru
mask:Form white].
mask displayOn:Display
at:(editorPen clipRect origin moveBy:(-1 @ -1))
clippingBox:editorPen clipRect
rule:Form orThru
mask:Form white.
gridForm
displayAt:editorPen clipRect origin
rule:Form andRule.
displayImage:anIcon
"Display anIcon in the image pane."
anIcon
displayAt:imagePen clipRect origin
rule:Form over.
eraseEditor
"Erase the editor pane."
Display white:editorPane frame.
eraseImage
"Erase the image pane."
Display white:imagePane frame.
getIcon
"Answer the currently displayed icon
taken from the image pane."
^((Form fromDisplay:imagePen clipRect)
offset:0 @ 0;
yourself).
"-----------------------------"
"-- Editing support methods --"
"-----------------------------"
setX:x Y:y
"Reverse a cell in both the Editor and
Icon Image panes."
editorPen
place:(self editorCellX:x y:y);
copyBits.
imagePen
place:(self imageCellX:x y:y);
copyBits.
editorCellX:x y:y
"Answer a Point with the location of the cell at
position x,y within the editor image."
^editorPen clipRect origin
+ ((x * scale @ (y * scale)) + cellOffset).
imageCellX:x y:y
"Answer a Point with the location of the cell at
position x,y within the image form."
^imagePen clipRect origin + (x @ y).
"-------------------------------"
"-- Reframing support methods --"
"-------------------------------"
reframeEditor:aFrame
"Reframe the editor pane to aFrame."
| w h yScale xScale penRect size |
w := aFrame width.
h := aFrame height.
xScale := w // (IconSize x + 2).
yScale := h // (IconSize y + 2).
scale := xScale min:yScale.
scale > 4
ifTrue:[
cellSize := (scale - 3) @ (scale - 3).
cellOffset := 2 @ 2]
ifFalse:[
cellSize := (scale - 2) @ (scale - 2).
cellOffset := 1 @ 1].
size := IconSize * scale.
gridForm := Form width:size x height:size y.
(Pen new:gridForm) grid:scale.
penRect := editorPane frame center - (size // 2) extent:size.
editorPen
clipRect:penRect;
defaultNib:cellSize;
combinationRule:Form reverse;
mask:nil.
reframeImage:aFrame
"Reframe the window's icon image pane to aFrame."
imagePen
clipRect:(aFrame center - (IconSize // 2)
extent:IconSize);
defaultNib:1 @ 1;
combinationRule:Form reverse;
mask:nil.
"-----------------------------"
"-- General support methods --"
"-----------------------------"
resizeIt:aSize
"Change the size of the icon to be aSize."
aSize = IconSize ifTrue:[^self].
(selectedIcon notNil
and:[selectedIcon extent ~= aSize])
ifTrue:[
selectedIcon := nil.
iconName := String new.
self
eraseEditor;
eraseImage.
listPane restoreSelected:iconName].
IconSize := aSize.
topPane reframe:topPane frame.
Scheduler resume.
saved
"If the image has not changed answer true;
otherwise ask user if changes are to be lost."
saved ifTrue:[^true].
(Menu message:'Image has changed -- discard changes?') isNil
ifTrue:[^false].
saved := true.
^saved.
saveIcon
"Save a modified icon image."
|name|
(name := Prompter
prompt: 'Enter name of new icon'
default: iconName) isNil
ifTrue:[^self].
(iconLibrary includesKey:name)
ifTrue:[
(Menu message:name, ' exists -- overwrite it?') isNil
ifTrue:[^self].
iconLibrary removeKey:name].
selectedIcon := self getIcon.
iconName := name.
iconLibrary at:name put:selectedIcon.
saved := true.
"----------------------------"
"-- Initialization methods --"
"----------------------------"
initLibrary
"Get the icon library to use."
(Smalltalk includesKey:#IconLibrary)
ifTrue:[
iconLibrary := Smalltalk at:#IconLibrary]
ifFalse:[
iconLibrary := Dictionary new.
Smalltalk at:#IconLibrary put:iconLibrary].
newIcon:anIcon
"Set up the editor and display a new icon."
selectedIcon := anIcon deepCopy.
self displayIcon.
[Listing -- Modified open Method]
openIt
"Open an IconEditor window."
| frame listWid listHgt |
iconLibrary isNil
ifTrue:[self initLibrary].
iconName := String new.
saved := true.
editorPen := Pen new.
imagePen := Pen new.
frame := (Display boundingBox extent // 6)
extent:(Display boundingBox extent * 2 // 3).
listWid := SysFont width * 10.
listHgt := SysFont height * 10.
topPane := TopPane new
label:self label;
model:self;
menu:#windowMenu;
minimumSize:frame extent;
yourself.
topPane addSubpane:
(listPane := ListPane new
model:self;
name:#iconList;
change:#iconSelection:;
returnIndex:false;
menu:#listMenu;
framingBlock:[:aFrame|
aFrame origin
extent:listWid @ listHgt];
yourself).
topPane addSubpane:
(imagePane := GraphPane new
model:self;
name:#initImage:;
menu:#noMenu;
framingBlock:[:aFrame|
aFrame origin + (0 @ listHgt)
extent:(listWid
@ (aFrame height - listHgt))];
yourself).
topPane addSubpane:
(editorPane := GraphPane new
model:self;
name:#initEditor:;
menu:#editorMenu;
change:#editIcon:;
framingBlock:[:aFrame|
aFrame origin + (listWid @ 0)
extent:((aFrame width - listWid)
@ aFrame height)];
yourself).
topPane reframe:frame.
topPane dispatcher openWindow scheduleWindow.